using System;

namespace CryptoLibrary.Symmetrical.Block
{
	public sealed class MC2 : BaseBlockCipher
	{
		#region tables

		private static readonly byte[] EXPF =
			{
				1, 45, 226, 147, 190, 69, 21, 174, 120, 3, 135, 164, 184, 56, 207, 63,
				8, 103, 9, 148, 235, 38, 168, 107, 189, 24, 52, 27, 187, 191, 114, 247,
				64, 53, 72, 156, 81, 47, 59, 85, 227, 192, 159, 216, 211, 243, 141, 177,
				255, 167, 62, 220, 134, 119, 215, 166, 17, 251, 244, 186, 146, 145, 100, 131,
				241, 51, 239, 218, 44, 181, 178, 43, 136, 209, 153, 203, 140, 132, 29, 20,
				129, 151, 113, 202, 95, 163, 139, 87, 60, 130, 196, 82, 92, 28, 232, 160,
				4, 180, 133, 74, 246, 19, 84, 182, 223, 12, 26, 142, 222, 224, 57, 252,
				32, 155, 36, 78, 169, 152, 158, 171, 242, 96, 208, 108, 234, 250, 199, 217,
				0, 212, 31, 110, 67, 188, 236, 83, 137, 254, 122, 93, 73, 201, 50, 194,
				249, 154, 248, 109, 22, 219, 89, 150, 68, 233, 205, 230, 70, 66, 143, 10,
				193, 204, 185, 101, 176, 210, 198, 172, 30, 65, 98, 41, 46, 14, 116, 80,
				2, 90, 195, 37, 123, 138, 42, 91, 240, 6, 13, 71, 111, 112, 157, 126,
				16, 206, 18, 39, 213, 76, 79, 214, 121, 48, 104, 54, 117, 125, 228, 237,
				128, 106, 144, 55, 162, 94, 118, 170, 197, 127, 61, 175, 165, 229, 25, 97,
				253, 77, 124, 183, 11, 238, 173, 75, 34, 245, 231, 115, 35, 33, 200, 5,
				225, 102, 221, 179, 88, 105, 99, 86, 15, 161, 49, 149, 23, 7, 58, 40
			};

		private static readonly byte[] LOGF =
			{
				128, 0, 176, 9, 96, 239, 185, 253, 16, 18, 159, 228, 105, 186, 173, 248,
				192, 56, 194, 101, 79, 6, 148, 252, 25, 222, 106, 27, 93, 78, 168, 130,
				112, 237, 232, 236, 114, 179, 21, 195, 255, 171, 182, 71, 68, 1, 172, 37,
				201, 250, 142, 65, 26, 33, 203, 211, 13, 110, 254, 38, 88, 218, 50, 15,
				32, 169, 157, 132, 152, 5, 156, 187, 34, 140, 99, 231, 197, 225, 115, 198,
				175, 36, 91, 135, 102, 39, 247, 87, 244, 150, 177, 183, 92, 139, 213, 84,
				121, 223, 170, 246, 62, 163, 241, 17, 202, 245, 209, 23, 123, 147, 131, 188,
				189, 82, 30, 235, 174, 204, 214, 53, 8, 200, 138, 180, 226, 205, 191, 217,
				208, 80, 89, 63, 77, 98, 52, 10, 72, 136, 181, 86, 76, 46, 107, 158,
				210, 61, 60, 3, 19, 251, 151, 81, 117, 74, 145, 113, 35, 190, 118, 42,
				95, 249, 212, 85, 11, 220, 55, 49, 22, 116, 215, 119, 167, 230, 7, 219,
				164, 47, 70, 243, 97, 69, 103, 227, 12, 162, 59, 28, 133, 24, 4, 29,
				41, 160, 143, 178, 90, 216, 166, 126, 238, 141, 83, 75, 161, 154, 193, 14,
				122, 73, 165, 44, 129, 196, 199, 54, 43, 127, 67, 149, 51, 242, 108, 104,
				109, 240, 2, 40, 206, 221, 155, 234, 94, 153, 124, 20, 134, 207, 229, 66,
				184, 64, 120, 45, 58, 233, 100, 31, 146, 144, 125, 57, 111, 224, 137, 48,
				128, 0, 176, 9, 96, 239, 185, 253, 16, 18, 159, 228, 105, 186, 173, 248,
				192, 56, 194, 101, 79, 6, 148, 252, 25, 222, 106, 27, 93, 78, 168, 130,
				112, 237, 232, 236, 114, 179, 21, 195, 255, 171, 182, 71, 68, 1, 172, 37,
				201, 250, 142, 65, 26, 33, 203, 211, 13, 110, 254, 38, 88, 218, 50, 15,
				32, 169, 157, 132, 152, 5, 156, 187, 34, 140, 99, 231, 197, 225, 115, 198,
				175, 36, 91, 135, 102, 39, 247, 87, 244, 150, 177, 183, 92, 139, 213, 84,
				121, 223, 170, 246, 62, 163, 241, 17, 202, 245, 209, 23, 123, 147, 131, 188,
				189, 82, 30, 235, 174, 204, 214, 53, 8, 200, 138, 180, 226, 205, 191, 217,
				208, 80, 89, 63, 77, 98, 52, 10, 72, 136, 181, 86, 76, 46, 107, 158,
				210, 61, 60, 3, 19, 251, 151, 81, 117, 74, 145, 113, 35, 190, 118, 42,
				95, 249, 212, 85, 11, 220, 55, 49, 22, 116, 215, 119, 167, 230, 7, 219,
				164, 47, 70, 243, 97, 69, 103, 227, 12, 162, 59, 28, 133, 24, 4, 29,
				41, 160, 143, 178, 90, 216, 166, 126, 238, 141, 83, 75, 161, 154, 193, 14,
				122, 73, 165, 44, 129, 196, 199, 54, 43, 127, 67, 149, 51, 242, 108, 104,
				109, 240, 2, 40, 206, 221, 155, 234, 94, 153, 124, 20, 134, 207, 229, 66,
				184, 64, 120, 45, 58, 233, 100, 31, 146, 144, 125, 57, 111, 224, 137, 48
			};


		private static readonly UInt64[] P = //first 256 prime numbers greater than 1,000,000
			{
				1000003, 1000033, 1000037, 1000039, 1000081, 1000099, 1000117, 1000121,
				1000133, 1000151, 1000159, 1000171, 1000183, 1000187, 1000193, 1000199,
				1000211, 1000213, 1000231, 1000249, 1000253, 1000273, 1000289, 1000291,
				1000303, 1000313, 1000333, 1000357, 1000367, 1000381, 1000393, 1000397,
				1000403, 1000409, 1000423, 1000427, 1000429, 1000453, 1000457, 1000507,
				1000537, 1000541, 1000547, 1000577, 1000579, 1000589, 1000609, 1000619,
				1000621, 1000639, 1000651, 1000667, 1000669, 1000679, 1000691, 1000697,
				1000721, 1000723, 1000763, 1000777, 1000793, 1000829, 1000847, 1000849,
				1000859, 1000861, 1000889, 1000907, 1000919, 1000921, 1000931, 1000969,
				1000973, 1000981, 1000999, 1001003, 1001017, 1001023, 1001027, 1001041,
				1001069, 1001081, 1001087, 1001089, 1001093, 1001107, 1001123, 1001153,
				1001159, 1001173, 1001177, 1001191, 1001197, 1001219, 1001237, 1001267,
				1001279, 1001291, 1001303, 1001311, 1001321, 1001323, 1001327, 1001347,
				1001353, 1001369, 1001381, 1001387, 1001389, 1001401, 1001411, 1001431,
				1001447, 1001459, 1001467, 1001491, 1001501, 1001527, 1001531, 1001549,
				1001551, 1001563, 1001569, 1001587, 1001593, 1001621, 1001629, 1001639,
				1001659, 1001669, 1001683, 1001687, 1001713, 1001723, 1001743, 1001783,
				1001797, 1001801, 1001807, 1001809, 1001821, 1001831, 1001839, 1001911,
				1001933, 1001941, 1001947, 1001953, 1001977, 1001981, 1001983, 1001989,
				1002017, 1002049, 1002061, 1002073, 1002077, 1002083, 1002091, 1002101,
				1002109, 1002121, 1002143, 1002149, 1002151, 1002173, 1002191, 1002227,
				1002241, 1002247, 1002257, 1002259, 1002263, 1002289, 1002299, 1002341,
				1002343, 1002347, 1002349, 1002359, 1002361, 1002377, 1002403, 1002427,
				1002433, 1002451, 1002457, 1002467, 1002481, 1002487, 1002493, 1002503,
				1002511, 1002517, 1002523, 1002527, 1002553, 1002569, 1002577, 1002583,
				1002619, 1002623, 1002647, 1002653, 1002679, 1002709, 1002713, 1002719,
				1002721, 1002739, 1002751, 1002767, 1002769, 1002773, 1002787, 1002797,
				1002809, 1002817, 1002821, 1002851, 1002853, 1002857, 1002863, 1002871,
				1002887, 1002893, 1002899, 1002913, 1002917, 1002929, 1002931, 1002973,
				1002979, 1003001, 1003003, 1003019, 1003039, 1003049, 1003087, 1003091,
				1003097, 1003103, 1003109, 1003111, 1003133, 1003141, 1003193, 1003199,
				1003201, 1003241, 1003259, 1003273, 1003279, 1003291, 1003307, 1003337
			};

		private static readonly UInt64[] C = //first 256 x 64-bit decimal digits of number e
			{
				0xb7e15162, 0x8aed2a6a, 0xbf715880, 0x9cf4f3c7,
				0x62e7160f, 0x38b4da56, 0xa784d904, 0x5190cfef,
				0x324e7738, 0x926cfbe5, 0xf4bf8d8d, 0x8c31d763,
				0xda06c80a, 0xbb1185eb, 0x4f7c7b57, 0x57f59584,
				0x90cfd47d, 0x7c19bb42, 0x158d9554, 0xf7b46bce,
				0xd55c4d79, 0xfd5f24d6, 0x613c31c3, 0x839a2ddf,
				0x8a9a276b, 0xcfbfa1c8, 0x77c56284, 0xdab79cd4,
				0xc2b3293d, 0x20e9e5ea, 0xf02ac60a, 0xcc93ed87,
				0x4422a52e, 0xcb238fee, 0xe5ab6add, 0x835fd1a0,
				0x753d0a8f, 0x78e537d2, 0xb95bb79d, 0x8dcaec64,
				0x2c1e9f23, 0xb829b5c2, 0x780bf387, 0x37df8bb3,
				0x00d01334, 0xa0d0bd86, 0x45cbfa73, 0xa6160ffe,
				0x393c48cb, 0xbbca060f, 0x0ff8ec6d, 0x31beb5cc,
				0xeed7f2f0, 0xbb088017, 0x163bc60d, 0xf45a0ecb,
				0x1bcd289b, 0x06cbbfea, 0x21ad08e1, 0x847f3f73,
				0x78d56ced, 0x94640d6e, 0xf0d3d37b, 0xe67008e1,
				0x86d1bf27, 0x5b9b241d, 0xeb64749a, 0x47dfdfb9,
				0x6632c3eb, 0x061b6472, 0xbbf84c26, 0x144e49c2,
				0xd04c324e, 0xf10de513, 0xd3f5114b, 0x8b5d374d,
				0x93cb8879, 0xc7d52ffd, 0x72ba0aae, 0x7277da7b,
				0xa1b4af14, 0x88d8e836, 0xaf14865e, 0x6c37ab68,
				0x76fe690b, 0x57112138, 0x2af341af, 0xe94f77bc,
				0xf06c83b8, 0xff5675f0, 0x979074ad, 0x9a787bc5,
				0xb9bd4b0c, 0x5937d3ed, 0xe4c3a793, 0x96215eda,
				0xb1f57d0b, 0x5a7db461, 0xdd8f3c75, 0x540d0012,
				0x1fd56e95, 0xf8c731e9, 0xc4d7221b, 0xbed0c62b,
				0xb5a87804, 0xb679a0ca, 0xa41d802a, 0x4604c311,
				0xb71de3e5, 0xc6b400e0, 0x24a6668c, 0xcf2e2de8,
				0x6876e4f5, 0xc50000f0, 0xa93b3aa7, 0xe6342b30,
				0x2a0a4737, 0x3b25f73e, 0x3b26d569, 0xfe2291ad,
				0x36d6a147, 0xd1060b87, 0x1a2801f9, 0x78376408,
				0x2ff592d9, 0x140db1e9, 0x399df4b0, 0xe14ca8e8,
				0x8ee9110b, 0x2bd4fa98, 0xeed150ca, 0x6dd89322,
				0x45ef7592, 0xc703f532, 0xce3a30cd, 0x31c070eb,
				0x36b4195f, 0xf33fb1c6, 0x6c7d70f9, 0x3918107c,
				0xe2051fed, 0x33f6d1de, 0x9491c7de, 0xa6a5a442,
				0xe154c8bb, 0x6d8d0362, 0x803bc248, 0xd414478c,
				0x2afb07ff, 0xe78e89b9, 0xfeca7e30, 0x60c08f0d,
				0x61f8e368, 0x01df66d1, 0xd8f9392e, 0x52caef06,
				0x53199479, 0xdf2be64b, 0xbaab008c, 0xa8a06fda,
				0xce9ce704, 0x89845a08, 0x2ba36d61, 0x1e99f2fb,
				0xe724246d, 0x18b54e33, 0x5cac0dd1, 0xab9dfd79,
				0x88a4b0c4, 0x558aa119, 0x417720b6, 0xe150ce2b,
				0x927d48d7, 0x256e445e, 0x333cb757, 0x2b3bd00f,
				0xb2746043, 0x189cac11, 0x6cedc7e7, 0x71ae0358,
				0xff752a3a, 0x6b6c79a5, 0x8a9a549b, 0x50c58706,
				0x90755c35, 0xe4e36b52, 0x9038ca73, 0x3fd1aaa8,
				0xdab40133, 0xd80320e0, 0x790968c7, 0x6546b993,
				0xf6c8ff3b, 0x2542750d, 0xa1ffada7, 0xb7473178,
				0x2e330ef7, 0xd92c43be, 0x1ad8c50a, 0x8eae20a5,
				0x556cbdd1, 0xf24c9997, 0x2cb03c73, 0x006f5c08,
				0xa4e220e7, 0x4abc1791, 0x51412b1e, 0x2dd60a08,
				0xa11b02e8, 0xd70d7d71, 0x64583301, 0x1bf60945,
				0x507f1a32, 0x721ac08a, 0xedc2661d, 0xa91839d1,
				0x46a2a4c4, 0x25c0ffb8, 0x7085f9b0, 0xe09b94b1,
				0x46a9a478, 0x3908f3f2, 0x67a78c59, 0x430485ed,
				0x89205b36, 0xb66a57e7, 0x56e00652, 0x23670282,
				0x87f8c1d6, 0x95df88c6, 0x0fe07528, 0xfcbe915c,
				0x7bf23382, 0xea293fa2, 0xda1577f9, 0xcac299bb,
				0x7b4beeaf, 0xef9628c3, 0xebeaf871, 0x75c6a1f8,
				0xbdd07be3, 0x07fa1bfa, 0x9aeff794, 0xc19dfc36,
				0x5f447527, 0xdea110f4, 0x208b941a, 0xa7d18538,
				0x0478aa52, 0x0e3fe233, 0x5a322edf, 0x147bbdb5,
				0x27aa2ad3, 0xcb0f7d6e, 0xd381cd6a, 0xc35a1d24
			};

		private static readonly UInt64[] Ts = //first 16 x 64-bit decimal digits of number Pi
			{
				0x243f6a8885a308d3, 0x13198a2e03707344, 0xa4093822299f31d0, 0x082efa98ec4e6c89,
				0x452821e638d01377, 0xbe5466cf34e90c6c, 0xc0ac29b7c97c50dd, 0x3f84d5b5b5470917,
				0x9216d5d98979fb1b, 0xd1310ba698dfb5ac, 0x2ffd72dbd01adfb7, 0xb8e1afed6a267e96,
				0xba7c9045f12c7f99, 0x24a19947b3916cf7, 0x0801f2e2858efc16, 0x636920d871574e69
			};

		private static UInt64[] T;

		#endregion

		private byte[] l_key;
		private byte[] temp;

		public MC2(string passphrase, ByteOrder order) : base(passphrase, order)
		{
		}

		public MC2(byte[] key, ByteOrder order) : base(key, order)
		{
		}

		~MC2()
		{
			Utility.Zeroize(l_key);
			Utility.Zeroize(temp);
		}

		protected override void Init()
		{
			l_key = new byte[16];
			temp = new byte[8];

			BLOCK_LENGTH = 16;
			name = "MC2";
		}

		private UInt64 F(UInt64 a, int round)
		{
			a += T[round]; //ADDING SALT VALUE
			a *= P[l_key[round]]; //Substitution over group

			a = (a << 45) | (a >> 19); //ROTR(a,19);

			temp[7] = LOGF[(byte) a];
			a >>= 8;
			temp[6] = EXPF[(byte) a];
			a >>= 8;
			temp[5] = LOGF[(byte) a];
			a >>= 8;
			temp[4] = EXPF[(byte) a];
			a >>= 8;
			temp[3] = LOGF[(byte) a];
			a >>= 8;
			temp[2] = EXPF[(byte) a];
			a >>= 8;
			temp[1] = LOGF[(byte) a];
			a >>= 8;
			temp[0] = EXPF[(byte) a];

			a = (((UInt64) temp[5] << 56) ^ ((UInt64) temp[1] << 48) ^ ((UInt64) temp[7] << 40) ^ ((UInt64) temp[3] << 32)) ^
				((UInt64) (temp[6] << 24) ^ ((UInt64) temp[4] << 16) ^ ((UInt64) temp[0] << 8) ^ ((UInt64) temp[2]));

			a = (a << 51) | (a >> 13); //ROTR(a,13);

			a += C[l_key[round]];

			return a;

		}

		private void RoundE(ref UInt64 L, ref UInt64 R, int round)
		{
			UInt64 swap;

			swap = L ^ F(R, round);
			L = R;
			R = swap;
		}

		private void RoundD(ref UInt64 L, ref UInt64 R, int round)
		{
			UInt64 swap;

			swap = R ^ F(L, round);
			R = L;
			L = swap;
		}

		protected override void set_key(byte[] key)
		{
			Array.Copy(key, l_key, 16);

			T = (UInt64[]) Ts.Clone();

			for (int i = 0; i < 16; i++)
				T[i] = F(T[i], i);
		}

		public override void E(byte[] input, int offsetI, byte[] output, int offsetO)
		{
			UInt64 L = Utility.ByteArrayToUInt64(input, offsetI, defaultByteOrder);
			UInt64 R = Utility.ByteArrayToUInt64(input, offsetI + 8, defaultByteOrder);

			for (int i = 0; i < 16; i++)
				RoundE(ref L, ref R, i);

			Utility.UInt64ToByteArray(L, output, offsetO, defaultByteOrder);
			Utility.UInt64ToByteArray(R, output, offsetO + 8, defaultByteOrder);
		}

		public override void E(Utility.PseudoCat input, int offsetI, byte[] output, int offsetO)
		{
			UInt64 L = Utility.ByteArrayToUInt64(input, offsetI, defaultByteOrder);
			UInt64 R = Utility.ByteArrayToUInt64(input, offsetI + 8, defaultByteOrder);

			for (int i = 0; i < 16; i++)
				RoundE(ref L, ref R, i);

			Utility.UInt64ToByteArray(L, output, offsetO, defaultByteOrder);
			Utility.UInt64ToByteArray(R, output, offsetO + 8, defaultByteOrder);
		}

		public override void D(byte[] input, int offsetI, byte[] output, int offsetO)
		{
			UInt64 L = Utility.ByteArrayToUInt64(input, offsetI, defaultByteOrder);
			UInt64 R = Utility.ByteArrayToUInt64(input, offsetI + 8, defaultByteOrder);

			for (int i = 15; i >= 0; i--)
				RoundD(ref L, ref R, i);

			Utility.UInt64ToByteArray(L, output, offsetO, defaultByteOrder);
			Utility.UInt64ToByteArray(R, output, offsetO + 8, defaultByteOrder);
		}

	}
}